package com.zero.ava.impl.kernel.schedule;

import com.zero.ava.auth.Identity;
import com.zero.ava.kernel.KernelRunnable;
import com.zero.ava.kernel.Priority;
import com.zero.ava.kernel.RecurringTaskHandle;
import com.zero.ava.kernel.TaskQueue;
import com.zero.ava.kernel.schedule.ScheduledTask;

final class ScheduledTaskImpl implements ScheduledTask {

	// the common, immutable aspects of a task
	private final KernelRunnable task;
	private final Identity owner;
	private final long period;

	// the common, mutable aspects of a task
	private volatile Priority priority;
	private volatile long startTime;

	private RecurringTaskHandle recurringTaskHandle = null;

	private int tryCount = 0;

	private TaskQueue queue = null;

	private volatile long timeout;

	private volatile Throwable lastFailure = null;

	/**
	 * 任务状态的描述
	 */
	private enum State {
		/* 可以被执行 */
		RUNNABLE,
		/* 执行中 */
		RUNNING,
		/* 被打断 */
		INTERRUPTED,
		/* 运行完成 */
		COMPLETED,
		/* 任务取消 */
		CANCELLED
	}

	private State state = State.RUNNABLE;

	// 任务的结果
	private Throwable result = null;

	/**
	 * 我们使用生成器模式在这里，以避免构造爆炸。此构建需要三个必要的参数，任务，所有者和优先级，
	 * 然后提供setter方法​​为每个构建ScheduledTaskImpl对象，其余可选参数。
	 * <p>
	 * Example usage:
	 * <p>
	 * 
	 * <pre>
	 * ScheduledTaskImpl task = new ScheduledTaskImpl.Builder(task, owner, priority).period(1000).build();
	 * </pre>
	 */
	static class Builder {
		// 用于构建ScheduledTaskImpl属性
		private KernelRunnable task;
		// 任务的拥有者
		private Identity owner;
		// 优先级
		private Priority priority;
		// 开始时间
		private long startTime = System.currentTimeMillis();
		// 周期
		private long period = NON_RECURRING;
		// 默认的超时
		private long timeout = defaultTimeout;
		// 周期任务
		private RecurringTaskHandle recurringTaskHandle = null;

		// 默认的超时
		private static long defaultTimeout = -2;

		/**
		 * 构造 {@code Builder} 对象，它有三个参数需要建立一个{@code ScheduledTaskImpl}.
		 * 
		 * @param task
		 *            the <code>KernelRunnable</code> to run
		 * @param owner
		 *            the <code>Identity</code> of the task owner
		 * @param priority
		 *            the <code>Priority</code> of the task
		 */
		Builder(KernelRunnable task, Identity owner, Priority priority) {
			this.task = task;
			this.owner = owner;
			this.priority = priority;
		}

		Builder(ScheduledTaskImpl task) {
			this(task.task, task.owner, task.priority);
			this.period = task.period;
			this.recurringTaskHandle = task.recurringTaskHandle;
		}

		/**
		 * 设定器，用于设定一个新{@code ScheduledTaskImpl}的开始时间。
		 * 
		 * @param startTime
		 *            the time at which to start in milliseconds since January
		 *            1, 1970
		 * @return this {@code Builder} object
		 */
		Builder startTime(long startTime) {
			this.startTime = startTime;
			return this;
		}

		/**
		 * 设定一个新的周期 {@code ScheduledTaskImpl}
		 * 
		 * @param period
		 *            the delay between recurring executions, or
		 *            {@code NON_RECURRING}
		 * @return this {@code Builder} object
		 */
		Builder period(long period) {
			this.period = period;
			return this;
		}

		/**
		 * 超时的设置 {@code ScheduledTaskImpl}
		 * 
		 * @param timeout
		 *            the transaction timeout to use for this task or
		 *            {@code UNBOUNDED}
		 * @return this {@code Builder} object
		 */
		Builder timeout(long timeout) {
			this.timeout = timeout;
			return this;
		}

		/**
		 * 设置默认的超时时间
		 * 
		 * @param timeout
		 *            default value of timeout
		 */
		static void setDefaultTimeout(long timeout) {
			defaultTimeout = timeout;
		}

		ScheduledTaskImpl build() {
			return new ScheduledTaskImpl(this);
		}

	}

	/**
	 * 从构建器中生成执行任务
	 * 
	 * @param builder
	 *            the {@code Builder} object that contains each of the desired
	 *            attributes of the new task
	 */
	private ScheduledTaskImpl(Builder builder) {
		if (builder.task == null) {
			throw new NullPointerException("Task cannot be null");
		}
		if (builder.owner == null) {
			throw new NullPointerException("Owner cannot be null");
		}
		if (builder.priority == null) {
			throw new NullPointerException("Priority cannot be null");
		}

		if (builder.timeout < 0 && builder.timeout != ScheduledTask.UNBOUNDED) {
			throw new IllegalStateException("Timeout cannot be negative");
		}

		this.task = builder.task;
		this.owner = builder.owner;
		this.priority = builder.priority;
		this.startTime = builder.startTime;
		this.period = builder.period;
		this.timeout = builder.timeout;
		this.recurringTaskHandle = builder.recurringTaskHandle;
	}

	/** Implementation of ScheduledTask interface. */

	/** {@inheritDoc} */
	public KernelRunnable getTask() {
		return task;
	}

	/** {@inheritDoc} */
	public Identity getOwner() {
		return owner;
	}

	/** {@inheritDoc} */
	public Priority getPriority() {
		return priority;
	}

	/** {@inheritDoc} */
	public long getStartTime() {
		return startTime;
	}

	/** {@inheritDoc} */
	public long getPeriod() {
		return period;
	}

	/** {@inheritDoc} */
	public int getTryCount() {
		return tryCount;
	}

	/** {@inheritDoc} */
	public long getTimeout() {
		return timeout;
	}

	/** {@inheritDoc} */
	public Throwable getLastFailure() {
		return lastFailure;
	}

	/** {@inheritDoc} */
	public void setPriority(Priority priority) {
		this.priority = priority;
	}

	/** {@inheritDoc} */
	public boolean isRecurring() {
		return period != NON_RECURRING;
	}

	/** {@inheritDoc} */
	public RecurringTaskHandle getRecurringTaskHandle() {
		return recurringTaskHandle;
	}

	/** {@inheritDoc} */
	public synchronized boolean isCancelled() {
		return state == State.CANCELLED;
	}

	/**
	 * {@inheritDoc}
	 */
	public synchronized boolean cancel(boolean block) {
		switch (state) {
		case COMPLETED:
		case CANCELLED:
			// 已经完成了，或者已经取消了
			return false;
		case RUNNING:
			if (block) {
				while (state == State.RUNNING) {
					// 如果任务已经在运行，我们只是等待它完成
					try {
						wait();
					} catch (InterruptedException ie) {
						// 忽略异常直到它运行完成
					}
				}
				if (!isDone()) {
					cancel();
					return true;
				}
			}
			return false;
		case RUNNABLE:
		case INTERRUPTED:
			// 被打断的直接取消
			cancel();
			return true;
		default:
			//这里应该进不来
			return false;
		}
	}

	/**
	 * Private cancel utility method used by {@link #cancel(boolean) cancel}.
	 */
	private void cancel() {
		state = State.CANCELLED;
		notifyAll();
	}

	/** Package-private utility methods. */

	/**
	 * Sets the transaction timeout for this task.
	 * 
	 * @param timeout
	 *            the new transaction timeout for this task
	 */
	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

	/**
	 * Returns {@code null} if the task completed successfully, or the
	 * {@code Throwable} that caused the task to fail permanently. If the task
	 * has not yet completed then this will block until the result is known or
	 * the caller is interrupted. An {@code InterruptedException} is thrown
	 * either if the calling thread is interrupted before a result is known or
	 * if the task is cancelled meaning that no result is known.
	 */
	synchronized Throwable get() throws InterruptedException {
		// wait for the task to finish
		while (!isDone()) {
			wait();
		}
		if (state == State.CANCELLED) {
			throw new InterruptedException("interrupted while getting result");
		}
		return result;
	}

	/** Re-sets the starting time to the now. */
	void resetStartTime() {
		startTime = System.currentTimeMillis();
	}

	/** Returns whether the task has finished. */
	synchronized boolean isDone() {
		return ((state == State.COMPLETED) || (state == State.CANCELLED));
	}

	/**
	 * Sets the state of this task to {@code running}, returning {@code false}
	 * if the task has already been cancelled or has completed, or if the state
	 * is not being changed.
	 */
	synchronized boolean setRunning(boolean running) {
		if (isDone()) {
			return false;
		}
		if (running) {
			if ((state != State.RUNNABLE) && (state != State.INTERRUPTED)) {
				return false;
			}
			state = State.RUNNING;
		} else {
			if (state != State.RUNNING) {
				return false;
			}
			state = State.RUNNABLE;
			notifyAll();
		}
		return true;
	}

	/**
	 * Similar to calling {@code setRunning(false)} except that no waiters are
	 * notified of the state change. This is used when the task's thread is
	 * interrupted and we don't know if the task is going to be re-runnable in a
	 * new thread or has to be dropped.
	 */
	synchronized boolean setInterrupted() {
		if (state == State.RUNNING) {
			state = State.INTERRUPTED;
			return true;
		}
		return false;
	}

	/** Returns whether this task is currently running. */
	synchronized boolean isRunning() {
		return state == State.RUNNING;
	}

	/**
	 * Sets the state of this task to done, with the result of the task being
	 * the provided {@code Throwable} which may be {@code null} to indicate that
	 * the task completed successfully. If the task is not currently running or
	 * was not previously interrupted then this method has no effect. Otherwise,
	 * the result is set and the task is marked as completed.
	 */
	synchronized void setDone(Throwable result) {
		if ((state != State.RUNNING) && (state != State.INTERRUPTED)) {
			return;
		}
		state = State.COMPLETED;
		this.result = result;
		notifyAll();
	}

	/**
	 * Sets this task's handle or throws {@code IllegalStateException} if the
	 * task is not recurring.
	 */
	void setRecurringTaskHandle(RecurringTaskHandle handle) {
		if (!isRecurring()) {
			throw new IllegalStateException("Not a recurring task");
		}
		recurringTaskHandle = handle;
	}

	/**
	 * Increments the try count (the number of times that this task has been
	 * attempted).
	 */
	void incrementTryCount() {
		tryCount++;
	}

	/**
	 * Sets the {@code Throwable} that caused the last failure of this task.
	 * 
	 * @param lastFailure
	 *            the last failure
	 */
	void setLastFailure(Throwable lastFailure) {
		this.lastFailure = lastFailure;
	}

	/**
	 * Sets the {@code TaskQueue} associated with this task. Typically this is
	 * used to track dependency, so that when this task is completed, the next
	 * task can be fetched from the queue.
	 */
	void setTaskQueue(TaskQueue queue) {
		this.queue = queue;
	}

	/**
	 * Returns the {@code TaskQueue} associated with this task or {@code null}
	 * if no queue was set. This is typically the source of dependent tasks
	 * where this task was submitted.
	 */
	TaskQueue getTaskQueue() {
		return queue;
	}

	/**
	 * Provides some diagnostic detail about this task.
	 * 
	 * @return a <code>String</code> representation of the task.
	 */
	public String toString() {
		return task.getBaseTaskType() + "[owner:" + owner.getName() + "]";
	}

}
